home *** CD-ROM | disk | FTP | other *** search
- ;; parseargs.e zilla oct92 - elkscheme commandline argument parser
- ;; (parseargs <argument-description> <body>)
- ;; Evaluates body in a let* context with command-line arguments parsed
- ;; and bound as described in argument-description.
- ;;
- ;; There are three types of arguments:
- ;; - required arguments
- ;; - flags, which look like "--flag"
- ;; - optional arguments, which look like "-opt <arg>"
- ;;
- ;; An argument description (argspec) is:
- ;; ([flag-string] [type] symbol [default-value]) or just 'symbol'
- ;; Flag-string is a string preceeding an optional value.
- ;; Type is one of integer,real,symbol.
- ;; Symbol is the symbol which will be bound to the command line argument.
- ;; Default-value is a default value for optional arguments.
- ;;
- ;; Flag-strings with one hyphen, e.g. "-n", should preceed a corresponding
- ;; argument. ("-n" integer n 0) would match ..."-n" "3"...
- ;; If the flag-string is not on the command line, the symbol is bound to #f.
- ;;
- ;; Flag arguments having two hyphens, e.g. "--v", are simply flags--
- ;; there is no associated argument. The symbol is bound to #t if
- ;; the flag is present, otherwise to #f.
- ;;
- ;; Example: with the command line arguments
- ;; ("foo.esh" "myfile" "3" "3.3" "--v" "-j" "11")
- ;; the call
- ;; (parseargs (fname
- ;; (integer ivar)
- ;; var
- ;; ("--v" flag)
- ;; ("-j" integer flagarg "5")
- ;; )
- ;; <body>)
- ;; expands to
- ;; (let* ((fname "myfile")
- ;; (ivar 3)
- ;; (var "3.3")
- ;; (flag #t)
- ;; (flagarg 11))
- ;; <body>)
- ;;
- ;; Note that the first command line argument (the name of the current
- ;; program) is ignored.
-
-
- (define-macro (parseargs . body)
- (let* ((args (car body))
- (body (cdr body))
- (bindings '())
- (clargs (cdr (command-line-args)))
- (clflagargs '())
- (clflags '())
- (clmainargs '())
- (clarg nil)
- ;(optional #f)
- )
-
- (define type-names '(integer real symbol))
-
- ;; put argspec in standard form (string|#f type|#f symbol default|#f)
- (define (cannonify arg)
- (let ((fullarg '())
- (str #f)
- (typ #f)
- (sym #f)
- (default #f)
- )
- (cond
- ((list? arg)
- (if (and (car arg) (string? (car arg)))
- (pop arg str))
- (if (and (car arg) (member (car arg) type-names))
- (pop arg typ))
- (if (not arg) (error 'parseargs "argspec missing symbol~%"))
- (pop arg sym)
- (if arg (set! default (car arg))))
-
- ((symbol? arg)
- (set! sym arg))
-
- (else (error 'parseargs "bad argspec: ~s" arg))
- );cond
-
- (list str typ sym default)
- )
- );cannonify
-
-
- (define (flag? arg)
- (let ((arg0 (list-ref arg 0)))
- (and (string? arg0) (equal? "--" (substring arg0 0 2)))))
-
- (define (optional? arg)
- (let ((arg0 (list-ref arg 0)))
- (and (string? arg0)
- (equal? "-" (substring arg0 0 1))
- (not (equal? "-" (substring arg0 1 2))))))
-
-
- ;; flag argument "--v", bind symbol to #t if present else #f
- (define (matchflag arg)
- (let* ((flagname (list-ref arg 0))
- (clflags (member flagname clflags))
- (sym (list-ref arg 2))
- )
- ;(format #t "flag search found ~a~%" clflags)
- (list sym (if clflags #t #f))
- )
- );flag #t/#f
-
-
- ;; optional argument e.g. ("-n" integer n 1)
- (define (matchoptional arg)
- (let* ((flag (list-ref arg 0))
- (clflagargs (member flag clflagargs))
- (clarg clarg)
- )
- ;(format #t "flag search found ~a~%" clargs)
- (if clflagargs (set! clflagargs (cdr clflagargs)))
- (if clflagargs (set! clarg (car clflagargs))
- (set! clarg #f))
- (if clarg
- (matcharg (cons #f (cdr arg)) clarg)
- (list (list-ref arg 2) #f))
- )
- );flag with argument
-
- (define (matchlist arg clarg)
- ;(format #t "matchlist arg=~a~%" arg)
- (let ((typ (list-ref arg 1))
- (sym (list-ref arg 2))
- (val #f)
- (default (list-ref arg 3))
- )
-
- (if (not clarg)
- (error 'parseargs "missing arg: ~a" sym))
-
- (set! val
- (case typ
- ((symbol) (string->symbol clarg))
- ((number integer real) (string->number clarg))
- ((#f) clarg)
- (else (error 'parsearg "unrecognized type: ~a" typ))))
- (list sym val)
- );let
- );matchlist
-
-
- ;; helper
- (define (matcharg arg clarg)
- ;(format #t "matcharg ~s ~s~%" arg clarg)
- (cond
-
- ((flag? arg)
- (matchflag arg))
-
- ((optional? arg)
- (matchoptional arg))
-
- (else
- (let ((m (matchlist arg clarg)))
- (if clmainargs (set! clmainargs (cdr clmainargs)))
- m))
-
-
- );cond
- );matcharg
-
-
- ;; split flag arguments and optional arguments from required arguments
- (while clargs
- (let ((clarg (car clargs)))
- (cond
- ((and (>= (string-length clarg) 2)
- (equal? "--" (substring clarg 0 2)))
- (set! clflags (cons clarg clflags)))
- ((equal? "-" (substring clarg 0 1))
- (set! clflagargs (cons clarg clflagargs))
- (set! clargs (cdr clargs))
- (if (not clargs) (error 'parseargs "-flag missing arg: ~a" clarg))
- (set! clflagargs (cons (car clargs) clflagargs)))
- (else
- (set! clmainargs (cons clarg clmainargs)))
- )
- (set! clargs (cdr clargs))
- );let
- );while
-
- (set! clmainargs (reverse! clmainargs))
- (set! clflagargs (reverse! clflagargs))
- (set! clargs (reverse! clargs))
- ;(format #t "clmainargs = ~s~%" clmainargs)
- ;(format #t "clflagargs = ~s~%" clflagargs)
- ;(format #t "clflags = ~s~%" clflags)
-
- (dolist (arg args)
- (set! arg (cannonify arg))
- (if clmainargs
- (set! clarg (car clmainargs))
- (set! clarg #f))
- (set! bindings (cons (matcharg arg clarg) bindings))
- ;(format #t "bindings=~s~%" bindings)
- );dolist
-
- `(let* ,(reverse! bindings)
- ,@body)
-
- );let
- );parseargs
-
-
- ;(define (command-line-args)
- ; (list "foo.esh" "foo.e" "-j" "133" "3" "3.3" )
- ; ;(list "foo.esh" "foo.e" "3" "3.3" "--v" "-j" "11")
- ;)
-
- ;(parseargs (fname
- ; (integer ivar)
- ; var
- ; ("--v" flag)
- ; ("-j" integer flagarg "5")
- ; )
- ; (format #t "RESULT: fname=~a, ivar=~a, var=~s flag=~s flagarg=~s~%"
- ; fname ivar var flag flagarg)
- ;)
-